package zack.gateway.center.domain.docker.service;

import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.async.ResultCallback;
import com.github.dockerjava.api.command.ExecCreateCmdResponse;
import com.github.dockerjava.core.DefaultDockerClientConfig;
import com.github.dockerjava.core.DockerClientBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import zack.gateway.center.application.ILoadBalancingService;
import zack.gateway.center.domain.docker.model.aggregates.NginxConfig;
import zack.gateway.center.domain.docker.model.vo.LocationVO;
import zack.gateway.center.domain.docker.model.vo.UpstreamVO;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author A1793
 */
@Service
public class LoadBalancingService extends AbstractLoadBalancingService{

    private Logger logger = LoggerFactory.getLogger(LoadBalancingService.class);

    @Value("${nginx.server_name}")
    private String serverName;

    @Override
    public String createNginxConfigFile(NginxConfig nginxConfig) throws IOException {
        String nginxConfigStr = buildNginxConfig(nginxConfig.getUpstreamList(),nginxConfig.getLocationList());
        File file = new File("/data/nginx/nginx.conf");
        if(!file.exists()){
            boolean success = file.createNewFile();
            if(success){
                logger.info("创建了新的nginx.conf文件");
            }else{
                logger.info("文件创建失败");
            }
        }
        FileWriter fileWriter = new FileWriter(file);
        fileWriter.write(nginxConfigStr);
        fileWriter.close();
        //返回文件的绝对路径
        return file.getAbsolutePath();

    }

    /**
     * docker容器reload
     * @param nginxName
     */
    @Override
    public void refreshNginxConfig(String nginxName) throws InterruptedException, IOException {
        //与docker的守护进程建立连接
        DefaultDockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder().
                withDockerHost("unix:///var/run/docker.sock").
                build();
        //对docker容器的操作都要通过这个client与docker守护进程之间的通信完成，client发出请求，守护进程接收到后执行操作
        DockerClient client = DockerClientBuilder.getInstance(config).build();
        //获取要操作的容器的id
        String containerId = client.listContainersCmd()
                //通过双括号快速初始化集合，实际是匿名内部类
                .withNameFilter(new ArrayList<String>(){{add(nginxName);}})
                .exec()
                .get(0)
                .getId();
        //docker主进程命令执行分为两部分，exec（创建命令计划）和start（实际执行）
        //在docker内存基于容器id建立命令执行计划
        ExecCreateCmdResponse execCreateCmdResponse = client
                .execCreateCmd(containerId)
                .withCmd("nginx","-s","reload")
                .exec();
        //发送请求，docker主进程实际执行reload命令
        client.execStartCmd(execCreateCmdResponse.getId()).
                exec(new ResultCallback.Adapter<>())
                .awaitCompletion();
        client.close();


    }
    private String buildNginxConfig(List<UpstreamVO> upstreams, List<LocationVO> locations) {
        //默认nginx配置
        String config = "\n" +
                "user  nginx;\n" +
                "worker_processes  auto;\n" +
                "\n" +
                "error_log  /var/log/nginx/error.log notice;\n" +
                "pid        /var/run/nginx.pid;\n" +
                "\n" +
                "\n" +
                "events {\n" +
                "    worker_connections  1024;\n" +
                "}\n" +
                "\n" +
                "\n" +
                "http {\n" +
                "    include       /etc/nginx/mime.types;\n" +
                "    default_type  application/octet-stream;\n" +
                "\n" +
                "    log_format  main  '$remote_addr - $remote_user [$time_local] \"$request\" '\n" +
                "                      '$status $body_bytes_sent \"$http_referer\" '\n" +
                "                      '\"$http_user_agent\" \"$http_x_forwarded_for\"';\n" +
                "\n" +
                "    access_log  /var/log/nginx/access.log  main;\n" +
                "\n" +
                "    sendfile        on;\n" +
                "    #tcp_nopush     on;\n" +
                "\n" +
                "    keepalive_timeout  65;\n" +
                "\n" +
                "    #gzip  on;\n" +
                "\n" +
                "    include /etc/nginx/conf.d/*.conf;\n" +
                "\n" +
                "    # 设定负载均衡的服务器列表\n" +
                "upstream_config_placeholder" +
                "\n" +
                "    # HTTP服务器\n" +
                "    server {\n" +
                "        # 监听80端口，用于HTTP协议\n" +
                "        listen  80;\n" +
                "\n" +
                "        # 定义使用IP/域名访问\n" +
                "        server_name "+ serverName+";\n" +
                "\n" +
                "        # 首页\n" +
                "        index index.html;\n" +
                "\n" +
                "        # 反向代理的路径（upstream绑定），location 后面设置映射的路径\n" +
                "\n" +
                "location_config_placeholder" +
                "    }\n" +
                "}\n";
        // 组装配置 Upstream
        StringBuilder upstreamStr = new StringBuilder();
        for (UpstreamVO upstream : upstreams) {
            upstreamStr.append("\t").append("upstream").append(" ").append(upstream.getName()).append(" {\r\n");
            upstreamStr.append("\t").append("\t").append(upstream.getStrategy()).append("\r\n").append("\r\n");
            List<String> servers = upstream.getServers();
            for (String server : servers) {
                upstreamStr.append("\t").append("\t").append("server").append(" ").append(server).append(";").append("\r\n");
            }
            upstreamStr.append("\t").append("}").append("\r\n").append("\r\n");
        }

        // 组装配置 Location
        StringBuilder locationStr = new StringBuilder();
        for (LocationVO location : locations) {
            locationStr.append("\t").append("\t").append("location").append(" ").append(location.getName()).append(" {\r\n");
            // rewrite ^/api01/(.*)$ /$1 break; 设置重写URL，在代理后去掉根路径 api01 此字段只是配合路由，不做处理
            locationStr.append("\t").append("\t").append("\t").append("rewrite ^").append(location.getName()).append("(.*)$ /$1 break;").append("\r\n");
            //设置转发路径
            locationStr.append("\t").append("\t").append("\t").append("proxy_pass").append(" ").append(location.getProxy_pass()).append("\r\n");
            //设置代理头
            locationStr.append("\t").append("\t").append("\t").append("proxy_set_header").append(" ").append("HOST $host;").append("\r\n");
            locationStr.append("\t").append("\t").append("\t").append("proxy_set_header").append(" ").append("X-Real-IP $remote_addr;").append("\r\n");
            locationStr.append("\t").append("\t").append("\t").append("proxy_set_header").append(" ").append("X-Forwarded-For $proxy_add_x_forwarded_for;").append("\r\n");
            locationStr.append("\t").append("\t").append("\t").append("proxy_set_header").append(" ").append("X-Forwarded-Proto $scheme;").append("\r\n");
            locationStr.append("\t").append("\t").append("}").append("\r\n").append("\r\n");
        }

        // 替换配置
        config = config.replace("upstream_config_placeholder", upstreamStr.toString());
        config = config.replace("location_config_placeholder", locationStr.toString());
        return config;
    }
}
